package org.cool.vnc;

import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.Executors;

import org.cool.vnc.proto.ProtocolVersion;
import org.cool.vnc.proto.RFBProtocolException;
import org.cool.vnc.proto.SecurityType;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Sets;

public class VNCClientHandler extends SimpleChannelHandler {

	private final Set<SecurityType> SUPPORTED_SECURITY_TYPES = Sets.newHashSet(
			SecurityType.None, SecurityType.VNC_AUTH, SecurityType.Tight,
			SecurityType.Ultra);

	private ProtocolVersion version = null;
	private SecurityType securityType = null;

	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
			throws Exception {
		ChannelBuffer buf = (ChannelBuffer) e.getMessage();
		System.out.println(buf);
		if (version == null && buf.readableBytes() >= 12) {
			ProtocolVersion serverVersion = ProtocolVersion.parse(buf);
			if (serverVersion.getMajor() < 3)
				throw new RFBProtocolException(
						"The version of RFB Server is not supported: "
								+ serverVersion);
			if (serverVersion.getMajor() > 3) {
				version = new ProtocolVersion(3, 8);
			} else if (serverVersion.getMinor() >= 8) {
				version = new ProtocolVersion(3, 8);
			} else if (serverVersion.getMinor() >= 7) {
				version = new ProtocolVersion(3, 7);
			} else if (serverVersion.getMinor() >= 3) {
				version = new ProtocolVersion(3, 3);
			} else {
				throw new RFBProtocolException(
						"The version of RFB Server is not supported: "
								+ serverVersion);
			}

			ChannelBuffer clientVerBuf = ChannelBuffers.dynamicBuffer();
			version.write(clientVerBuf);
			e.getChannel().write(clientVerBuf);
			System.out.println("Handshaking Protocol: " + version);
			return;
		}

		if (securityType == null) {
			SecurityType[] serverSecurityTypes = SecurityType.parse(buf);
			Collection<SecurityType> supportedTypes = Collections2.filter(
					Arrays.asList(serverSecurityTypes),
					new Predicate<SecurityType>() {
						public boolean apply(SecurityType t) {
							return SUPPORTED_SECURITY_TYPES.contains(t);
						}
					});
			
		}
	}

	public static void main(String[] args) {
		ChannelFactory factory = new NioClientSocketChannelFactory(
				Executors.newCachedThreadPool(),
				Executors.newCachedThreadPool());
		ClientBootstrap bootstrap = new ClientBootstrap(factory);
		bootstrap.setPipelineFactory(new ChannelPipelineFactory() {

			@Override
			public ChannelPipeline getPipeline() throws Exception {
				return Channels.pipeline(new VNCClientHandler());
			}
		});

		bootstrap.setOption("tcpNoDelay", true);
		bootstrap.setOption("keepAlive", true);
		bootstrap.connect(new InetSocketAddress("192.168.60.201", 5900));
	}
}
